﻿using System;
using System.Collections.Generic;
using System.Linq;
using Helpers;
using MarriageFertility.Helpers;
using MarriageFertility.Mcm;
using MarriageFertility.Texts;
using TaleWorlds.CampaignSystem;
using TaleWorlds.CampaignSystem.Actions;
using TaleWorlds.CampaignSystem.CharacterDevelopment;
using TaleWorlds.CampaignSystem.Conversation;
using TaleWorlds.CampaignSystem.Conversation.Persuasion;
using TaleWorlds.Core;
using TaleWorlds.Localization;

namespace MarriageFertility.Dialogs;

public static class PoetryDialog
{
    private static List<PersuasionTask> _allPoetryTasks;
    private static float _maximumScoreCap;
    public static Dictionary<string, CampaignTime> PoetryTimes = new();
    private static readonly Dictionary<Hero, bool> PoetryPersuasionStarted = new();
    private static readonly List<PoetryAttempt> PreviousPoetryAttempts = new();

    private const PersuasionDifficulty _difficulty = PersuasionDifficulty.Medium;
    private const float _successValue = 0.8f;
    private const float _criticalSuccessValue = 1.5f;
    private const float _criticalFailValue = 2.0f;
    private const float _failValue = 0.4f;
    private const int _maxTasks = 5;
    private const int _poemsPerTask = 6;

    public enum PoetryTheme
    {
        FrequencyPositive,
        FrequencyNegative,
        MercyPositive,
        MercyNegative,
        ValorPositive,
        ValorNegative,
        HonorPositive,
        HonorNegative,
        GenerosityPositive,
        GenerosityNegative,
        CalculatingPositive,
        CalculatingNegative
    }

    public static void AddDialogs(CampaignGameStarter campaignGameStarter)
    {
        AddPursueWithPoetryDialogs(campaignGameStarter);
    }

    private static void AddPursueWithPoetryDialogs(CampaignGameStarter campaignGameStarter)
    {
        campaignGameStarter.AddPlayerLine("pursue_with_poetry_start", "hero_main_options", "pursue_with_poetry_start_response",
            PoetryText.PoemStart.ToString(), CanPursueWithPoetry, StartPoetryPersuasion, 100);

        campaignGameStarter.AddDialogLine("pursue_with_poetry_start_response", "pursue_with_poetry_start_response", "pursue_with_poetry_choice",
            PoetryText.LordAskPoem.ToString(), null, null, 100);

        campaignGameStarter.AddDialogLine("pursue_with_poetry_fail", "pursue_with_poetry_choice", "hero_main_options",
            "{=!}{FAILED_POETRY_LINE}", HasFailedPoetry, EndPoetryPersuasion, 200);

        campaignGameStarter.AddDialogLine("pursue_with_poetry_success", "pursue_with_poetry_choice", "hero_main_options",
            "{=!}{SUCCESS_RESPONSE}", CheckIfPoetrySuccessful, ApplyPoetrySuccess, 200);
        
        for (int optionIndex = 0; optionIndex < _poemsPerTask; optionIndex++)
        {  
            int localIndex = optionIndex; // 创建局部变量
            string optionId = $"poem_option_{localIndex}";
            campaignGameStarter.AddPlayerLine(optionId, "pursue_with_poetry_choice", $"pursue_with_poetry_reaction_{localIndex}",
                "{=!}{POETRY_LINE_" + localIndex + "}",
                () => CheckPoemOption(localIndex),
                () => ApplyPoemOption(localIndex),
                100,
                delegate(out TextObject hintText) { return CheckPoemOptionClickable(localIndex, out hintText); },
                () => SetupPoemOption(localIndex));

            campaignGameStarter.AddDialogLine($"npc_reaction_{localIndex}", $"pursue_with_poetry_reaction_{localIndex}", "pursue_with_poetry_choice",
                "{=!}{POETRY_REACTION}",
                () => CheckPoetryReaction(localIndex),
                () => ApplyPoetryReaction(localIndex));
        }

        campaignGameStarter.AddPlayerLine("pursue_with_poetry_no_answer", "pursue_with_poetry_choice", "pursue_with_poetry_no_answer_response",
            PoetryText.NoAnswer.ToString(), null, null, 100);

        campaignGameStarter.AddDialogLine("pursue_with_poetry_no_answer_response", "pursue_with_poetry_no_answer_response", "hero_main_options",
            PoetryText.NoAnswerResponse.ToString(), null, EndPoetryPersuasion, 100);
    }

    private static bool CanPursueWithPoetry()
    {
        Hero hero = Hero.OneToOneConversationHero;
        if (hero == null || !MCMSettings.Instance.EnablePoemDialog || !hero.IsAlive || hero.IsPlayerSpouse())
            return false;

        if (PoetryTimes.TryGetValue(hero.StringId, out CampaignTime campaignTime))
        {
            return CampaignTime.Now > campaignTime + CampaignTime.Days(1);
        }

        return true;
    }

    private static void StartPoetryPersuasion()
    {
        Hero hero = Hero.OneToOneConversationHero;
        if (hero == null || PoetryPersuasionStarted.ContainsKey(hero)) return;

        PoetryPersuasionStarted[hero] = true;
        _allPoetryTasks = GetPoetryTasksForHero(hero, Hero.MainHero);
        if (!_allPoetryTasks.Any())
        {
            Log.CommonMessage(PoetryText.NoPoetryTasks.SetTextVariable("HERO", hero.Name), TextColor.Red);
            EndPoetryPersuasion();
            return;
        }

        _maximumScoreCap = _maxTasks;
        float initialProgress = CalculateInitialProgress();
        ConversationManager.StartPersuasion(_maximumScoreCap, _successValue, _failValue, _criticalSuccessValue, _criticalFailValue, initialProgress, _difficulty);
    }

    private static float CalculateInitialProgress()
    {
        float progress = 0f;
        foreach (var task in _allPoetryTasks)
        {
            foreach (var attempt in PreviousPoetryAttempts)
            {
                if (attempt.Matches(Hero.OneToOneConversationHero, task.ReservationType))
                {
                    switch (attempt.Result)
                    {
                        case PersuasionOptionResult.CriticalFailure: progress -= _criticalFailValue; break;
                        case PersuasionOptionResult.Failure: progress -= _failValue; break;
                        case PersuasionOptionResult.Success: progress += _successValue; break;
                        case PersuasionOptionResult.CriticalSuccess: progress += _criticalSuccessValue; break;
                    }
                }
            }
        }
        RemoveOldAttempts();
        return progress;
    }

    private static void RemoveOldAttempts()
    {
        PreviousPoetryAttempts.RemoveAll(a => a.GameTime.ElapsedYearsUntilNow > 1f || !a.PersuadedHero.IsAlive);
    }

    private static bool CanAttemptPoetry(Hero targetHero, int theme)
    {
        return !PreviousPoetryAttempts.Any(a => a.Matches(targetHero, theme) && !a.IsSuccessful() && a.GameTime.ElapsedWeeksUntilNow < 1f);
    }

    private static void ApplyPoetrySuccess()
    {
        Hero hero = Hero.OneToOneConversationHero;
        if (hero != null)
        {
            PoetryTimes[hero.StringId] = CampaignTime.Now;
            TextObject message = PoetryText.PoetrySuccessMessage;
            message.SetTextVariable("HERO", hero.Name);
            MBInformationManager.AddQuickInformation(message);
            PoetryPersuasionStarted.Remove(hero);
        }
        _allPoetryTasks = null;
        ConversationManager.EndPersuasion();
    }

    private static void EndPoetryPersuasion()
    {
        Hero hero = Hero.OneToOneConversationHero;
        if (hero != null)
        {
            PoetryTimes[hero.StringId] = CampaignTime.Now;
            TextObject message = PoetryText.PoetryFailMessage;
            message.SetTextVariable("HERO", hero.Name);
            MBInformationManager.AddQuickInformation(message);
            PoetryPersuasionStarted.Remove(hero);
        }
        _allPoetryTasks = null;
        ConversationManager.EndPersuasion();
    }

    private static int CalculateRelationChange(Hero hero, int poemIndex, bool isCritical, bool isSuccess = true)
    {
        if (poemIndex < 0) return isSuccess ? (isCritical ? 15 : 10) : (isCritical ? -10 : -5);

        var poem = PoetryText.PoemsWithDetails[poemIndex];
        int traitValue = GetTraitValue(hero, poem.Theme);
        float relation = hero.GetRelationWithPlayer();
        if (isSuccess)
        {
            if (traitValue > 0) // Perfect match
                return isCritical ? 25 : 15;
            if (traitValue == 0 || relation >= 50) // Partial match
                return isCritical ? 20 : 10;
            return isCritical ? 10 : 5; // No match
        }
        else
        {
            if (traitValue > 0) // Perfect match but failed
                return isCritical ? -10 : -5;
            if (traitValue == 0 || relation >= 50) // Partial match
                return isCritical ? -10 : -5;
            return isCritical ? -15 : -10; // No match
        }
    }

    private static int GetTraitValue(Hero hero, PoetryTheme theme)
    {
        switch (theme)
        {
            case PoetryTheme.FrequencyPositive: return hero.GetTraitLevel(DefaultTraits.Frequency);
            case PoetryTheme.FrequencyNegative: return -hero.GetTraitLevel(DefaultTraits.Frequency);
            case PoetryTheme.MercyPositive: return hero.GetTraitLevel(DefaultTraits.Mercy);
            case PoetryTheme.MercyNegative: return -hero.GetTraitLevel(DefaultTraits.Mercy);
            case PoetryTheme.ValorPositive: return hero.GetTraitLevel(DefaultTraits.Valor);
            case PoetryTheme.ValorNegative: return -hero.GetTraitLevel(DefaultTraits.Valor);
            case PoetryTheme.HonorPositive: return hero.GetTraitLevel(DefaultTraits.Honor);
            case PoetryTheme.HonorNegative: return -hero.GetTraitLevel(DefaultTraits.Honor);
            case

 PoetryTheme.GenerosityPositive: return hero.GetTraitLevel(DefaultTraits.Generosity);
            case PoetryTheme.GenerosityNegative: return -hero.GetTraitLevel(DefaultTraits.Generosity);
            case PoetryTheme.CalculatingPositive: return hero.GetTraitLevel(DefaultTraits.Calculating);
            case PoetryTheme.CalculatingNegative: return -hero.GetTraitLevel(DefaultTraits.Calculating);
            default: return 0;
        }
    }

    private static bool HasFailedPoetry()
    {
        var task = GetCurrentPoetryTask();
        if (task?.Options == null || !task.Options.Any())
        {
            SelectRandomRejectResponse();
            return true;
        }

        if (task.Options.All(o => o.IsBlocked) && !ConversationManager.GetPersuasionProgressSatisfied())
        {
            SelectRandomRejectResponse();
            return true;
        }
        return false;
    }

    private static bool CheckIfPoetrySuccessful()
    {
        bool isSuccess = ConversationManager.GetPersuasionProgressSatisfied();
        if (isSuccess)
        {
            SelectRandomSuccessResponse();
        }
        return isSuccess;
    }

    private static void SelectRandomSuccessResponse()
    {
        var successResponses = new List<TextObject>
        {
            PoetryText.SuccessResponse1, PoetryText.SuccessResponse2, PoetryText.SuccessResponse3, PoetryText.SuccessResponse4
        };
        MBTextManager.SetTextVariable("SUCCESS_RESPONSE", successResponses[MBRandom.RandomInt(successResponses.Count)]);
    }

    private static void SelectRandomRejectResponse()
    {
        var rejectResponses = new List<TextObject>
        {
            PoetryText.RejectResponse1, PoetryText.RejectResponse2, PoetryText.RejectResponse3, PoetryText.RejectResponse4
        };
        MBTextManager.SetTextVariable("FAILED_POETRY_LINE", rejectResponses[MBRandom.RandomInt(rejectResponses.Count)]);
    }

    private static PersuasionTask GetCurrentPoetryTask()
    {
        foreach (var task in _allPoetryTasks)
        {
            if (!task.Options.All(o => o.IsBlocked))
                return task;
        }
        return _allPoetryTasks.Last();
    }

    private static List<PersuasionTask> GetPoetryTasksForHero(Hero target, Hero initiator)
    {
        var tasks = new List<PersuasionTask>();
        StringHelpers.SetCharacterProperties("PLAYER", initiator.CharacterObject);
        float relationship = target.GetRelationWithPlayer();

        var allThemes = new[]
        {
            (PoetryTheme.FrequencyPositive, DefaultTraits.Frequency, PoetryText.FrequencyPositiveSpoken, PoetryText.FrequencyPositiveImmediateFail, PoetryText.FrequencyPositiveFail, PoetryText.FrequencyPositiveTryLater),
            (PoetryTheme.FrequencyNegative, DefaultTraits.Frequency, PoetryText.FrequencyNegativeSpoken, PoetryText.FrequencyNegativeImmediateFail, PoetryText.FrequencyNegativeFail, PoetryText.FrequencyNegativeTryLater),
            (PoetryTheme.MercyPositive, DefaultTraits.Mercy, PoetryText.MercyPositiveSpoken, PoetryText.MercyPositiveImmediateFail, PoetryText.MercyPositiveFail, PoetryText.MercyPositiveTryLater),
            (PoetryTheme.MercyNegative, DefaultTraits.Mercy, PoetryText.MercyNegativeSpoken, PoetryText.MercyNegativeImmediateFail, PoetryText.MercyNegativeFail, PoetryText.MercyNegativeTryLater),
            (PoetryTheme.ValorPositive, DefaultTraits.Valor, PoetryText.ValorPositiveSpoken, PoetryText.ValorPositiveImmediateFail, PoetryText.ValorPositiveFail, PoetryText.ValorPositiveTryLater),
            (PoetryTheme.ValorNegative, DefaultTraits.Valor, PoetryText.ValorNegativeSpoken, PoetryText.ValorNegativeImmediateFail, PoetryText.ValorNegativeFail, PoetryText.ValorNegativeTryLater),
            (PoetryTheme.HonorPositive, DefaultTraits.Honor, PoetryText.HonorPositiveSpoken, PoetryText.HonorPositiveImmediateFail, PoetryText.HonorPositiveFail, PoetryText.HonorPositiveTryLater),
            (PoetryTheme.HonorNegative, DefaultTraits.Honor, PoetryText.HonorNegativeSpoken, PoetryText.HonorNegativeImmediateFail, PoetryText.HonorNegativeFail, PoetryText.HonorNegativeTryLater),
            (PoetryTheme.GenerosityPositive, DefaultTraits.Generosity, PoetryText.GenerosityPositiveSpoken, PoetryText.GenerosityPositiveImmediateFail, PoetryText.GenerosityPositiveFail, PoetryText.GenerosityPositiveTryLater),
            (PoetryTheme.GenerosityNegative, DefaultTraits.Generosity, PoetryText.GenerosityNegativeSpoken, PoetryText.GenerosityNegativeImmediateFail, PoetryText.GenerosityNegativeFail, PoetryText.GenerosityNegativeTryLater),
            (PoetryTheme.CalculatingPositive, DefaultTraits.Calculating, PoetryText.CalculatingPositiveSpoken, PoetryText.CalculatingPositiveImmediateFail, PoetryText.CalculatingPositiveFail, PoetryText.CalculatingPositiveTryLater),
            (PoetryTheme.CalculatingNegative, DefaultTraits.Calculating, PoetryText.CalculatingNegativeSpoken, PoetryText.CalculatingNegativeImmediateFail, PoetryText.CalculatingNegativeFail, PoetryText.CalculatingNegativeTryLater)
        };

        // Randomly select up to 5 themes
        var availableThemes = allThemes.Where(t => CanAttemptPoetry(target, (int)t.Item1)).ToList();
        var selectedThemes = availableThemes.OrderBy(_ => MBRandom.RandomFloat).Take(_maxTasks).ToList();

        foreach (var (theme, trait, spoken, immediateFail, finalFail, tryLater) in selectedThemes)
        {
            var task = new PersuasionTask((int)theme)
            {
                SpokenLine = spoken,
                ImmediateFailLine = immediateFail,
                FinalFailLine = finalFail,
                TryLaterLine = tryLater
            };
            
            var themePoems = PoetryText.PoemsWithDetails.Where(p => p.Theme == theme).Take(2).ToList();
            var remainingCount = _poemsPerTask - themePoems.Count;
            var otherPoems = PoetryText.PoemsWithDetails
                .Where(p => p.Theme != theme)
                .OrderBy(_ => MBRandom.RandomFloat)
                .Take(remainingCount)
                .ToList();
            var taskPoems = themePoems.Concat(otherPoems).Take(_poemsPerTask).ToList();

            // Ensure exactly 6 poems
            if (taskPoems.Count < _poemsPerTask)
            {
                var additionalPoems = PoetryText.PoemsWithDetails
                    .OrderBy(_ => MBRandom.RandomFloat)
                    .Take(_poemsPerTask - taskPoems.Count)
                    .ToList();
                taskPoems.AddRange(additionalPoems);
            }

            foreach (var poem in taskPoems)
            {
                PersuasionArgumentStrength strength;
                int traitValue = GetTraitValue(target, poem.Theme);
                if (traitValue > 0 && poem.Theme == theme)
                    strength = relationship >= 50 ? PersuasionArgumentStrength.Easy : PersuasionArgumentStrength.Normal;
                else if (traitValue == 0 || relationship >= 50)
                    strength = PersuasionArgumentStrength.Hard;
                else
                    strength = PersuasionArgumentStrength.VeryHard;

                task.AddOptionToTask(new PersuasionOptionArgs(
                    DefaultSkills.Charm, trait, TraitEffect.Positive, strength, true,
                    poem.Line, null, false, true, false));
            }
            
            tasks.Add(task);
        }
        
        if (!tasks.Any())
        {
            var defaultTask = new PersuasionTask(-1)
            {
                SpokenLine = PoetryText.DefaultSpoken,
                ImmediateFailLine = PoetryText.DefaultImmediateFail,
                FinalFailLine = PoetryText.DefaultFail,
                TryLaterLine = PoetryText.DefaultTryLater
            };

            var defaultPoems = PoetryText.PoemsWithDetails.OrderBy(_ => MBRandom.RandomFloat).Take(_poemsPerTask).ToList();
            foreach (var poem in defaultPoems)
            {
                defaultTask.AddOptionToTask(new PersuasionOptionArgs(
                    DefaultSkills.Charm, DefaultTraits.Calculating, TraitEffect.Positive, PersuasionArgumentStrength.Hard, true,
                    poem.Line, null, false, true, false));
            }
            tasks.Add(defaultTask);
        }

        return tasks;
    }

    private static bool CheckPoemOption(int index)
    {
        var task = GetCurrentPoetryTask();
        if (index >= task.Options.Count) index = 0;
        if (task == null || index < 0)
        {
            return false;
        }
        var text = PoetryText.PoemOption;
        text.SetTextVariable("SUCCESS_CHANCE", PersuasionHelper.ShowSuccess(task.Options[index]));
        text.SetTextVariable("PERSUASION_OPTION_LINE", task.Options[index].Line);
        MBTextManager.SetTextVariable("POETRY_LINE_" + index, text);
        return true;
    }

    private static bool CheckPoemOptionClickable(int index, out TextObject hintText)
    {
        hintText = TextObject.Empty;
        var task = GetCurrentPoetryTask();
        if (task == null || index >= task.Options.Count || index < 0)
        {
            hintText = PoetryText.Blocked;
            return false;
        }

        if (task.Options[index].IsBlocked)
        {
            hintText = PoetryText.Blocked;
            return false;
        }

        return true;
    }

    private static void ApplyPoemOption(int index)
    {
        var task = GetCurrentPoetryTask();
        if (index < task.Options.Count && index >= 0)
        {
            task.Options[index].BlockTheOption(true);
        }
    }

    private static PersuasionOptionArgs SetupPoemOption(int index)
    {
        var task = GetCurrentPoetryTask();
        return index < task.Options.Count && index >= 0 ? task.Options[index] : null;
    }

    private static bool CheckPoetryReaction(int index)
    {
        var result = ConversationManager.GetPersuasionChosenOptions().Last().Item2;
        var task = GetCurrentPoetryTask();
        if (index >= task.Options.Count || index < 0)
        {
            MBTextManager.SetTextVariable("POETRY_REACTION", PoetryText.ReactionFailure);
            return true;
        }

        switch (result)
        {
            case PersuasionOptionResult.CriticalFailure:
                MBTextManager.SetTextVariable("POETRY_REACTION", task.ImmediateFailLine ?? PoetryText.ReactionCriticalFailure);
                _allPoetryTasks.ForEach(t => t.BlockAllOptions());
                break;
            case PersuasionOptionResult.Failure:
                MBTextManager.SetTextVariable("POETRY_REACTION", task.ImmediateFailLine ?? PoetryText.ReactionFailure);
                break;
            case PersuasionOptionResult.Success:
            case PersuasionOptionResult.CriticalSuccess:
                var poemIndex = PoetryText.PoemsWithDetails.FindIndex(p => p.Line == task.Options[index].Line);
                MBTextManager.SetTextVariable("POETRY_REACTION", poemIndex >= 0 ? PoetryText.PoemsWithDetails[poemIndex].Response : PoetryText.ReactionFailure);
                break;
            default:
                MBTextManager.SetTextVariable("POETRY_REACTION", PersuasionHelper.GetDefaultPersuasionOptionReaction(result));
                break;
        }
        return true;
    }

    private static void ApplyPoetryReaction(int index)
    {
        var task = GetCurrentPoetryTask();
        if (index >= task.Options.Count || index < 0)
        {
            return;
        }

        var chosenOption = ConversationManager.GetPersuasionChosenOptions().Last();
        float difficulty = Campaign.Current.Models.PersuasionModel.GetDifficulty(_difficulty);
        float moveToNextStageChance, blockRandomOptionChance;
        Campaign.Current.Models.PersuasionModel.GetEffectChances(chosenOption.Item1, out moveToNextStageChance, out blockRandomOptionChance, difficulty);
        task.ApplyEffects(moveToNextStageChance, blockRandomOptionChance);
        PreviousPoetryAttempts.Add(new PoetryAttempt(Hero.OneToOneConversationHero, CampaignTime.Now, chosenOption.Item1, chosenOption.Item2, task.ReservationType));
        
        Hero hero = Hero.OneToOneConversationHero;
        if (hero != null)
        {
            int poemIndex = PoetryText.PoemsWithDetails.FindIndex(p => p.Line == chosenOption.Item1.Line);
            bool isCritical = chosenOption.Item2 == PersuasionOptionResult.CriticalSuccess || chosenOption.Item2 == PersuasionOptionResult.CriticalFailure;
            bool isSuccess = chosenOption.Item2 == PersuasionOptionResult.Success || chosenOption.Item2 == PersuasionOptionResult.CriticalSuccess;
            int relationChange = CalculateRelationChange(hero, poemIndex, isCritical, isSuccess);

            ChangeRelationAction.ApplyPlayerRelation(hero, relationChange);
            
            TextObject message;
            if (relationChange >= 0)
            {
                message = PoetryText.PoetryAttemptSuccess;
            }
            else
            {
                message = PoetryText.PoetryAttemptFail;
            }
            message.SetTextVariable("HERO", hero.Name);
            message.SetTextVariable("RELATION", Math.Abs(relationChange));
            MBInformationManager.AddQuickInformation(message);
        }
    }
}

public class PoetryAttempt
{
    public Hero PersuadedHero { get; }
    public CampaignTime GameTime { get; }
    public PersuasionOptionArgs Option { get; }
    public PersuasionOptionResult Result { get; }
    public int Theme { get; }

    public PoetryAttempt(Hero persuadedHero, CampaignTime gameTime, PersuasionOptionArgs option, PersuasionOptionResult result, int theme)
    {
        PersuadedHero = persuadedHero;
        GameTime = gameTime;
        Option = option;
        Result = result;
        Theme = theme;
    }

    public bool Matches(Hero hero, int theme) => PersuadedHero == hero && Theme == theme;
    public bool IsSuccessful() => Result == PersuasionOptionResult.Success || Result == PersuasionOptionResult.CriticalSuccess;
}